home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
graphics
/
font3d10.zip
/
truetype.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-15
|
40KB
|
954 lines
//===========================================================================================================
// TrueType.CPP
//
// Copyright (c) 1994 by Todd A. Prater
// All rights reserved.
//
//===========================================================================================================
#include <stdlib.h>
#include <fstream.h>
#include "Config.H"
#include "Array.H"
#include "Geometry.H"
#include "TrueType.H"
//===========================================================================================================
// TTFont (PUBLIC)
//-----------------------------------------------------------------------------------------------------------
// This is the constructor for the TTFont class. It opens the font file given by 'filename', and reads
// the information it needs into its internal data structures.
//===========================================================================================================
TTFont::TTFont(CHARPTR filename)
{
ULONG c,i,j,k,gi; // A few counters.
ULONG pos; // A placekeeper for the fontdata array.
LONG dx,dy,tx,ty; // Used in translating from relative to absolute coordinates.
USHORT tabledirsize; // Size of the Table Directory.
ULONG tabledatasize; // Size of the data contained in all the tables combined.
ULONG fontdatasize; // Size of the entire font file.
ULONG tempts; // Temporarily holds the length of a table.
BYTE* tfd; // This will temporarily hold the first twelve bytes of the font file.
BYTE* tabledir; // This will temporarily hold the table directory of the font.
ULONG cmapTableOffset; // Offset of the 'cmap' table
ULONG cmapTableLength; // Length of the 'cmap' table
ULONG glyfTableOffset; // Offset of the 'glyf' table
ULONG glyfTableLength; // Length of the 'glyf' table
ULONG headTableOffset; // Offset of the 'head' table
ULONG headTableLength; // Length of the 'head' table
ULONG locaTableOffset; // Offset of the 'loca' table
ULONG locaTableLength; // Length of the 'loca' table
ULONG maxpTableOffset; // Offset of the 'maxp' table
ULONG maxpTableLength; // Length of the 'maxp' table
ULONG nameTableOffset; // Offset of the 'name' table
ULONG nameTableLength; // Length of the 'name' table
Fixed sfnt_version; // Used to read the Offset Table
USHORT numTables; // .
USHORT searchRange; // .
USHORT entrySelector; // .
USHORT rangeShift;
SHORT indexToLocFormat; // Which format the data in the 'loca' table is in (SHORT or LONG)
USHORT numNameRecords; // Used in decoding the naming table 'name'
USHORT stringStorageOffset; // .
USHORT platformID; // .
USHORT specificID; // .
USHORT languageID;
USHORT nameID;
USHORT stringLength;
USHORT stringOffset;
ULONG offset; // Used in decoding the glyph offset table
ULONG* glyphOffsetArray;
USHORT numCmapSubtables; // Used in decoding the 'cmap' table
ULONG cmapSubtableOffset; // .
ULONG cmapSubtableStart; // .
USHORT cmapSubtableLength; // .
USHORT cmapSubtableSegCount;
USHORT cmapSubtableLastCharacter;
USHORT* cmapSubtableEndCount;
USHORT* cmapSubtableStartCount;
USHORT* cmapSubtableIdDelta;
USHORT* cmapSubtableIdRangeOffset;
ULONG cmapSubtableIdRangeOffsetArrayAddress;
ULONG index;
ULONG seg;
BYTE found;
USHORT repeatcount; // Used in decoding the 'glyf' table
USHORT* endPtsOfContours; // .
SHORT numberOfContours; // .
SHORT xMin; // .
SHORT yMin;
SHORT xMax;
SHORT yMax;
USHORT numberOfPoints;
USHORT instructionLength;
BYTE* flags;
SHORT xWord;
SHORT yWord;
SHORT xByte;
SHORT yByte;
SHORT lastx;
SHORT lasty;
USHORT startPoint;
USHORT endPoint;
BYTE currentFlag;
ULONG endOfXData;
BYTE tempbyte;
CHAR tempchar;
SHORT tempshort;
USHORT tempushort;
LONG templong;
ULONG tempulong;
Fixed tempfixed;
copyrightString=NULL;
familyString=NULL;
subfamilyString=NULL;
idString=NULL;
fullnameString=NULL;
versionString=NULL;
psnameString=NULL;
trademarkString=NULL;
// ____ Create an input file stream, associate it with the font file we're reading in, ____
// ____ and set it to read in binary mode. Also, make sure it doesn't ignore spaces. ____
#ifdef __GNUC__
ifstream inputfile((char*)filename);
#else
ifstream inputfile((char*)filename,ios::binary);
#endif
if (!inputfile)
{
cout<<"ERROR: Unable to open '"<<filename<<"'\n";
exit(1);
}
inputfile.unsetf(ios::skipws);
// ____ Create an array to temporarily hold the Offset Table. ____
tfd = new BYTE[OFFSET_TABLE_SIZE];
// ____ Read in the Offset Table of the font file. Among other things, it contains ____
// ____ the number of table entries (and tables) to follow. ____
for (i=0;i<OFFSET_TABLE_SIZE;i++)
inputfile>>tfd[i];
// ____ Together, bytes 4 and 5 describe the number of entries that follow in the ____
// ____ Table Directory. ____
numTables = toUSHORT(tfd[4],tfd[5]);
// ____ Now that we know how many tables there are, we can calculate the size of the ____
// ____ Table Directory, since each entry contains four ULONG's. ____
tabledirsize = sizeof(ULONG)*4*numTables;
// ____ And, make an array to hold the Table Directory data temporarily. ____
tabledir = new BYTE[tabledirsize]; // delete this...
// ____ Next, store the Table Directory in the temporary array. Each entry contains ____
// ____ the length of that particular table, so keep track of the combined length of ____
// ____ all the tables while we're at it. Each loop iteration reads in 16 bytes. ____
// ____ Note that all tables are long aligned in the file itself, but the lengths ____
// ____ given in the Table Directory are the actual lengths of the table data. Any ____
// ____ length not a multiple of four must be rounded up. ____
tabledatasize=0;
for (i=0;i<tabledirsize;i=i+16)
{
inputfile>>tabledir[i]>>tabledir[i+1]>>tabledir[i+2]>>tabledir[i+3];
inputfile>>tabledir[i+4]>>tabledir[i+5]>>tabledir[i+6]>>tabledir[i+7];
inputfile>>tabledir[i+8]>>tabledir[i+9]>>tabledir[i+10]>>tabledir[i+11];
inputfile>>tabledir[i+12]>>tabledir[i+13]>>tabledir[i+14]>>tabledir[i+15];
tempts = toULONG(tabledir[i+12],tabledir[i+13],tabledir[i+14],tabledir[i+15]);
if ((tempts/4)*4 != tempts) tempts = (tempts/4+1)*4;
tabledatasize += tempts;
}
// ____ Now we know enough information to calculate the size of the entire font file. ____
fontdatasize = OFFSET_TABLE_SIZE+tabledirsize+tabledatasize;
// ____ And, create an array to hold the entire font file. ____
#ifdef __MSDOS__
ARRAY<BYTE> fontdata(fontdatasize);
#else
BYTE* fontdata = new BYTE[fontdatasize];
#endif
// ____ Put all of the information we temporarily stored, into the new font data ____
// ____ array, and delete the temporary storage space since we won't need it anymore. ____
for (i=0;i<OFFSET_TABLE_SIZE;i++)
fontdata[i]=tfd[i];
for (i=OFFSET_TABLE_SIZE;i<OFFSET_TABLE_SIZE+tabledirsize;i++)
fontdata[i]=tabledir[i-OFFSET_TABLE_SIZE];
delete tfd;
// ____ Read in the rest of the font file. ____
for (i=OFFSET_TABLE_SIZE+tabledirsize;i<fontdatasize;i++)
{
inputfile>>fontdata[i];
}
// ____ Close the font file. ____
inputfile.close();
// ____ At this point, we have read the font file into memory. Now we turn to the ____
// ____ task of deciphering it.... ____
pos = 0;
// ____ The only thing we need in the Offset Table is the number of tables, which we ____
// ____ we have already found, so skip over it now. ____
pos+=sizeof(Fixed);
pos+=4*sizeof(USHORT);
// ____ The only tables we will need to look at are: cmap, glyf, head, loca, maxp, ____
// ____ and name. Since the entries in the Table Directory are in alphabetical ____
// ____ order, we can search for them one at a time, getting the information in each ____
// ____ one as necessary. All of the tables are required, so they SHOULD be there. ____
while ( fontdata[pos] !='c' // Search for the 'cmap' table entry
|| fontdata[pos+1]!='m'
|| fontdata[pos+2]!='a'
|| fontdata[pos+3]!='p') pos+=16;
cmapTableOffset = toULONG(fontdata[pos+8],fontdata[pos+9],fontdata[pos+10],fontdata[pos+11]);
cmapTableLength = toULONG(fontdata[pos+12],fontdata[pos+13],fontdata[pos+14],fontdata[pos+15]);
pos+=16;
while ( fontdata[pos] !='g' // Search for the 'glyf' table entry
|| fontdata[pos+1]!='l'
|| fontdata[pos+2]!='y'
|| fontdata[pos+3]!='f') pos+=16;
glyfTableOffset = toULONG(fontdata[pos+8],fontdata[pos+9],fontdata[pos+10],fontdata[pos+11]);
glyfTableLength = toULONG(fontdata[pos+12],fontdata[pos+13],fontdata[pos+14],fontdata[pos+15]);
pos+=16;
while ( fontdata[pos] !='h' // Search for the 'head' table entry
|| fontdata[pos+1]!='e'
|| fontdata[pos+2]!='a'
|| fontdata[pos+3]!='d') pos+=16;
headTableOffset = toULONG(fontdata[pos+8],fontdata[pos+9],fontdata[pos+10],fontdata[pos+11]);
headTableLength = toULONG(fontdata[pos+12],fontdata[pos+13],fontdata[pos+14],fontdata[pos+15]);
pos+=16;
while ( fontdata[pos] !='l' // Search for the 'loca' table entry
|| fontdata[pos+1]!='o'
|| fontdata[pos+2]!='c'
|| fontdata[pos+3]!='a') pos+=16;
locaTableOffset = toULONG(fontdata[pos+8],fontdata[pos+9],fontdata[pos+10],fontdata[pos+11]);
locaTableLength = toULONG(fontdata[pos+12],fontdata[pos+13],fontdata[pos+14],fontdata[pos+15]);
pos+=16;
while ( fontdata[pos] !='m' // Search for the 'maxp' table entry
|| fontdata[pos+1]!='a'
|| fontdata[pos+2]!='x'
|| fontdata[pos+3]!='p') pos+=16;
maxpTableOffset = toULONG(fontdata[pos+8],fontdata[pos+9],fontdata[pos+10],fontdata[pos+11]);
maxpTableLength = toULONG(fontdata[pos+12],fontdata[pos+13],fontdata[pos+14],fontdata[pos+15]);
pos+=16;
while ( fontdata[pos] !='n' // Search for the 'name' table entry
|| fontdata[pos+1]!='a'
|| fontdata[pos+2]!='m'
|| fontdata[pos+3]!='e') pos+=16;
nameTableOffset = toULONG(fontdata[pos+8],fontdata[pos+9],fontdata[pos+10],fontdata[pos+11]);
nameTableLength = toULONG(fontdata[pos+12],fontdata[pos+13],fontdata[pos+14],fontdata[pos+15]);
pos+=16;
// ____ Now we know the offset and length of each of the tables we will need in the ____
// ____ font file. Now we can start the processing of each table. ____
//==========================================================================================
// ____ We begin with the Font Header Table: 'head'. The only two pieces of informa- ____
// ____ tion we need are 'indexToLocFormat, which specifies the data type of the ____
// ____ offsets used in the 'loca' table, and 'unitsPerEm' which specifies the number ____
// ____ of units in the 'Em Square'. ____
//==========================================================================================
pos = headTableOffset + 18;
unitsPerEm = toUSHORT(fontdata[pos],fontdata[pos+1]);
pos = headTableOffset + 50;
indexToLocFormat = toSHORT(fontdata[pos],fontdata[pos+1]);
//==========================================================================================
// ____ Next is the Maximum Profile Table: 'maxp'. The values we need here are the ____
// ____ number of glyphs in the font file, the maximum number of points per glyph, ____
// ____ and the maximum number of contours per glyph. ____
//==========================================================================================
pos = maxpTableOffset + 4;
numGlyphs = toUSHORT(fontdata[pos],fontdata[pos+1]);
// ____ Go ahead and create the glyph array now, we'll fill it in later. ____
glyphs = new TTGlyph[numGlyphs];
pos = maxpTableOffset + 6;
maxPoints = toUSHORT(fontdata[pos],fontdata[pos+1]);
pos = maxpTableOffset + 8;
maxContours = toUSHORT(fontdata[pos],fontdata[pos+1]);
//==========================================================================================
// ____ Third, is the Naming Table: 'name'. There are up to eight strings contained ____
// ____ here: copyright notice, font family name, font subfamily name, a unique font ____
// ____ identifier, full font name, version, postscript name, and trademark. Memory ____
// ____ for each string is allocated as needed. ____
//==========================================================================================
// ____ Find the number of name records (ie. strings) in the Naming Table. ____
pos = nameTableOffset + 2;
numNameRecords = toUSHORT(fontdata[pos],fontdata[pos+1]);
// ____ Find the offset from the beginning of the Naming Table to the beginning of the ____
// ____ string storage area. ____
pos = nameTableOffset + 4;
stringStorageOffset = toUSHORT(fontdata[pos],fontdata[pos+1]);
// ____ Go through each name record in the table and find the ones that are for ____
// ____ Windows/OS/2 computers (platformID==3), ASCII (specificID==1), and in English ____
// ____ (languageID==1033). Once we have one that fits that description, determine ____
// ____ which of the eight possible name strings it is (ie. copyright, family, etc...) ____
// ____ and make a null-terminated, ASCII string out of it. All the strings that have ____
// ____ a platformID of 3 are in Unicode (two-byte characters), but, for our purposes, ____
// ____ we can just use the low byte for each character. This is because ASCII and ____
// ____ Unicode match for most ASCII characters. In the code, this is done by ignor- ____
// ____ ing every other byte in the string. ____
pos = nameTableOffset + 6;
while (pos<(nameTableOffset+stringStorageOffset))
{
platformID = toUSHORT(fontdata[pos ],fontdata[pos+1 ]);
specificID = toUSHORT(fontdata[pos+2 ],fontdata[pos+3 ]);
languageID = toUSHORT(fontdata[pos+4 ],fontdata[pos+5 ]);
nameID = toUSHORT(fontdata[pos+6 ],fontdata[pos+7 ]);
stringLength = toUSHORT(fontdata[pos+8 ],fontdata[pos+9 ]);
stringOffset = toUSHORT(fontdata[pos+10],fontdata[pos+11]);
if ( platformID==3 && specificID==1 && languageID==1033 )
{
switch (nameID)
{
case 0:
copyrightString = new CHAR[stringLength/2+1];
for(i=1;i<stringLength;i+=2)
copyrightString[i/2]=fontdata[nameTableOffset+stringStorageOffset+stringOffset+i];
copyrightString[i/2]=0;
break;
case 1:
familyString = new CHAR[stringLength/2+1];
for(i=1;i<stringLength;i+=2)
familyString[i/2]=fontdata[nameTableOffset+stringStorageOffset+stringOffset+i];
familyString[i/2]=0;
break;
case 2:
subfamilyString = new CHAR[stringLength/2+1];
for(i=1;i<stringLength;i+=2)
subfamilyString[i/2]=fontdata[nameTableOffset+stringStorageOffset+stringOffset+i];
subfamilyString[i/2]=0;
break;
case 3:
idString = new CHAR[stringLength/2+1];
for(i=1;i<stringLength;i+=2)
idString[i/2]=fontdata[nameTableOffset+stringStorageOffset+stringOffset+i];
idString[i/2]=0;
break;
case 4:
fullnameString = new CHAR[stringLength/2+1];
for(i=1;i<stringLength;i+=2)
fullnameString[i/2]=fontdata[nameTableOffset+stringStorageOffset+stringOffset+i];
fullnameString[i/2]=0;
break;
case 5:
versionString = new CHAR[stringLength/2+1];
for(i=1;i<stringLength;i+=2)
versionString[i/2]=fontdata[nameTableOffset+stringStorageOffset+stringOffset+i];
versionString[i/2]=0;
break;
case 6:
psnameString = new CHAR[stringLength/2+1];
for(i=1;i<stringLength;i+=2)
psnameString[i/2]=fontdata[nameTableOffset+stringStorageOffset+stringOffset+i];
psnameString[i/2]=0;
break;
case 7:
trademarkString = new CHAR[stringLength/2+1];
for(i=1;i<stringLength;i+=2)
trademarkString[i/2]=fontdata[nameTableOffset+stringStorageOffset+stringOffset+i];
trademarkString[i/2]=0;
break;
}
}
pos+=12;
}
//==========================================================================================
// ____ Fourth, is the Index to Location Table: 'loca'. It stores the offsets to each ____
// ____ of the glyphs in the font, relative to the beginning of the Glyph Data Table. ____
// ____ Also implied in this information is the length of each of the glyph elements, ____
// ____ and for this reason, there is an extra number at the end of the table that ____
// ____ gives the length of the last glyph. There are two versions of this table, one ____
// ____ short and one long. Which version is present is specified in the ____
// ____ indexToLocFormat entry in the 'head' table. ____
//==========================================================================================
pos=locaTableOffset;
glyphOffsetArray = new ULONG[numGlyphs+1];
for (i=0;i<numGlyphs+1;i++)
{
if (indexToLocFormat==0)
{
// __ In the short format, offset/2 is stored, so offset*2 is the actual offset __
offset=(ULONG)toUSHORT(fontdata[pos],fontdata[pos+1]);
offset=offset*2;
pos+=2;
}
else
{
// __ In the long format, the actual offset is stored __
offset=toULONG(fontdata[pos],fontdata[pos+1],fontdata[pos+2],fontdata[pos+3]);
pos+=4;
}
glyphOffsetArray[i]=offset;
}
//==========================================================================================
// ____ Fifth, is the Character to Glyph Index Mapping Table: 'cmap'. This table ____
// ____ defines the mapping of character codes to the glyph index values used in the ____
// ____ font. Although more than one mapping may be present, we will only use the ____
// ____ Microsoft UGL encoding subtable. ____
//==========================================================================================
// ____ Get the number of subtables in this table. ____
pos = cmapTableOffset+2;
numCmapSubtables = toUSHORT(fontdata[pos],fontdata[pos+1]);
// ____ Look through the list of subtable entries for one with a platformID of 3, and ____
// ____ a specificID of 1. This is the table with Microsoft UGL encodings. ____
found = 0;
pos = cmapTableOffset+4;
while (pos<(cmapTableOffset+4+numCmapSubtables*8))
{
platformID=toUSHORT(fontdata[pos],fontdata[pos+1]);
specificID=toUSHORT(fontdata[pos+2],fontdata[pos+3]);
cmapSubtableOffset = toULONG(fontdata[pos+4],fontdata[pos+5],fontdata[pos+6],fontdata[pos+7]);
if (platformID==3 && specificID==1)
{
found=1;
break;
}
else pos+=8;
}
if (!found)
{
cout<<"ERROR: This font file doesn't have a Microsoft UGL character encoding table."<<endl;
cout<<" Unable to continue. Aborting."<<endl;
exit(1);
}
// ____ Calculate the offset of the start of the Microsoft UGL subtable ____
cmapSubtableStart = cmapTableOffset+cmapSubtableOffset;
// ____ Find the length of the subtable, and the number of segments in it. ____
pos = cmapSubtableStart+2;
cmapSubtableLength = toUSHORT(fontdata[pos],fontdata[pos+1]);
pos = cmapSubtableStart+6;
cmapSubtableSegCount = toUSHORT(fontdata[pos],fontdata[pos+1])/2;
// ____ Read in the array of Ending Character codes for each of the segments. ____
cmapSubtableEndCount = new USHORT[cmapSubtableSegCount];
pos = cmapSubtableStart+14;
for(i=0;i<cmapSubtableSegCount*2;i+=2)
cmapSubtableEndCount[i/2]=toUSHORT(fontdata[pos+i],fontdata[pos+i+1]);
pos+=i;
pos+=2; // Eat up the reserved padding word.
// ____ Read in the array of Beginning Character codes for each of the segments ____
cmapSubtableStartCount = new USHORT[cmapSubtableSegCount];
for(i=0;i<cmapSubtableSegCount*2;i+=2)
cmapSubtableStartCount[i/2]=toUSHORT(fontdata[pos+i],fontdata[pos+i+1]);
pos+=i;
// ____ Read in the Delta value for each segment. ____
cmapSubtableIdDelta = new USHORT[cmapSubtableSegCount];
for(i=0;i<cmapSubtableSegCount*2;i+=2)
cmapSubtableIdDelta[i/2]=toUSHORT(fontdata[pos+i],fontdata[pos+i+1]);
pos+=i;
// ____ Read in the Offset into the GlyphIdArray for each segment. ____
cmapSubtableIdRangeOffsetArrayAddress = pos;
cmapSubtableIdRangeOffset = new USHORT[cmapSubtableSegCount];
for(i=0;i<cmapSubtableSegCount*2;i+=2)
cmapSubtableIdRangeOffset[i/2]=toUSHORT(fontdata[pos+i],fontdata[pos+i+1]);
pos+=i;
// ____ Now use the values we've just looked up to calculate the glyph index of each ____
// ____ character 0-255. Details on how this is done can be found in the technical ____
// ____ description of the font file given in 'TrueType Font Files' by the Microsoft ____
// ____ Corporation. ____
for(c=0;c<256;c++)
{
for(seg=0;seg<cmapSubtableSegCount;seg++)
if(cmapSubtableEndCount[seg]>=c) break;
if (cmapSubtableStartCount[seg]>c)
characterMap[c]=0;
else
{
if (cmapSubtableIdRangeOffset[seg]==0)
characterMap[c] = (USHORT)(cmapSubtableIdDelta[seg]+c);
else
{
index = cmapSubtableIdRangeOffset[seg]
+ (c-cmapSubtableStartCount[seg])*2
+ cmapSubtableIdRangeOffsetArrayAddress+seg*2;
characterMap[c] = toUSHORT(fontdata[index],fontdata[index+1]);
}
}
}
delete cmapSubtableEndCount; // We won't be needing these anymore.
delete cmapSubtableStartCount;
delete cmapSubtableIdDelta;
delete cmapSubtableIdRangeOffset;
//==========================================================================================
// ____ Finally, our last table contains the glyph data: 'glyf'. ____
//==========================================================================================
// ___ Allocate enough space for all the glyphs in the font ____
glyphs = new TTGlyph[numGlyphs];
if (!glyphs)
{
cout<<"ERROR: Out of memory."<<endl;
exit(1);
}
for(gi=0;gi<numGlyphs;gi++)
{
// ____ If the offset of this glyph is the same as the next one, then that means ____
// ____ the glyph has no contours. We can go ahead and skip the rest of this ____
// ____ iteration. Otherwise, calculate the glyph's offset into the font file. ____
if ((gi<numGlyphs-1) && (glyphOffsetArray[gi]==glyphOffsetArray[gi+1]))
continue;
else
pos = glyfTableOffset + glyphOffsetArray[gi];
// ____ Read the glyph header. ____
numberOfContours = toSHORT(fontdata[pos],fontdata[pos+1]); pos+=2;
xMin = toSHORT(fontdata[pos],fontdata[pos+1]); pos+=2;
yMin = toSHORT(fontdata[pos],fontdata[pos+1]); pos+=2;
xMax = toSHORT(fontdata[pos],fontdata[pos+1]); pos+=2;
yMax = toSHORT(fontdata[pos],fontdata[pos+1]); pos+=2;
// ____ Fill in the glyph's data fields. ____
glyphs[gi].numContours = numberOfContours;
glyphs[gi].xMin = xMin;
glyphs[gi].yMin = yMin;
glyphs[gi].xMax = xMax;
glyphs[gi].yMax = yMax;
// ____ Only continue processing this glyph if it is a simple glyph (not a ____
// ____ composite). ____
if (numberOfContours>0)
{
// ____ Create an array to hold the end points of all the contours in the glyph. ____
endPtsOfContours = new USHORT[numberOfContours];
if(!endPtsOfContours)
{
cout<<"ERROR: Out of memory."<<endl;
exit(1);
}
// ____ Read in the end point information from the glyph table. ____
for(i=0;i<numberOfContours;i++)
{
endPtsOfContours[i]=toUSHORT(fontdata[pos],fontdata[pos+1]); pos+=2;
}
// ____ Allocate memory for each of the contours in the glyph. ____
glyphs[gi].contours = new TTContour[numberOfContours];
if(!glyphs[gi].contours)
{
cout<<"ERROR: Out of memory."<<endl;
exit(1);
}
// ____ Calculate the total number of points in this glyph (all contours). ____
numberOfPoints = endPtsOfContours[numberOfContours-1]+1;
// ____ Read in the length of the instructions contained in the table for this ____
// ____ particular glyph. ____
instructionLength = toUSHORT(fontdata[pos],fontdata[pos+1]); pos+=2;
// ____ Skip over the instructions. ____
pos+=instructionLength;
// ____ Allocate space for an array of flags, one for each point in the glyph. ____
flags = new BYTE[numberOfPoints];
if(!flags)
{
cout<<"ERROR: Out of memory."<<endl;
exit(1);
}
// ____ Read in the flags for this glyph. The outer loop gathers the flags that ____
// ____ are actually contained in the table. If the repeat bit is set in a flag ____
// ____ then the next byte is read from the table; this is the number of times ____
// ____ to repeat the last flag. The inner loop does this, incrementing the ____
// ____ outer loops index each time. ____
for(i=0;i<numberOfPoints;i++)
{
flags[i]=fontdata[pos]; pos++;
if (isBitSet(flags[i],3))
{
repeatcount=fontdata[pos];pos++;
for(;repeatcount>0;repeatcount--)
{
i++;
flags[i]=flags[i-1];
}
}
}
// ____ Now come the x- and y-coordinate data. The font file stores all coord- ____
// ____ inates as relative to the previous one. This is how we read them in ____
// ____ initially; we will convert to absolute coords later. The x- and y- ____
// ____ coordinates come separately, so we will have to have separate loops for ____
// ____ each one. Each outer loop iteration will read in a contour, while the ____
// ____ inner loop reads in each point. ____
lastx=0; // The first coordinate is relative to 0. ____
startPoint = 0; // Start with the first point in the glyph. ____
for(i=0;i<numberOfContours;i++)
{
// ____ Find the end point of this contour. ____
endPoint = endPtsOfContours[i];
// ____ Store the number of points in this contour. ____
glyphs[gi].contours[i].numPoints = endPoint-startPoint+1;
// ____ Allocate space for the points in this contour. ____
glyphs[gi].contours[i].points = new TTPoint[endPoint-startPoint+1];
if(!glyphs[gi].contours[i].points)
{
cout<<"ERROR: Out of memory."<<endl;
exit(1);
}
// ____ Get each point in this contour. Coordinates in the font file are ____
// ____ indexed according to position in the entire glyph, while the ____
// ____ coordinates stored are indexed according to their position in the ____
// ____ current contour. 'startPoint' and 'endPoint' are glyph indexes, and ____
// ____ j is the index into the current contour. ____
// ____ Process X-Coordinates. ____
for(j=startPoint;j<=endPoint;j++)
{
// ____ Get the current flag. ____
currentFlag = flags[j];
// ____ If bit zero in the flag is set then this point is an on-curve ____
// ____ point, if not, then it is an off-curve point. ____
if(isBitSet(currentFlag,0))
glyphs[gi].contours[i].points[j-startPoint].type = ON_CURVE;
else
glyphs[gi].contours[i].points[j-startPoint].type = OFF_CURVE;
// ____ First we check to see if bit one is set. This would indicate that ____
// ____ the corresponding coordinate data in the table is 1 byte long. ____
// ____ If the bit is not set, then the coordinate data is 2 bytes long. ____
if(isBitSet(currentFlag,1))
{
// ____ Read in one byte. ____
xByte = fontdata[pos];pos++;
// ____ In this case, bit four is the sign of the byte just read in. ____
if(isBitSet(currentFlag,4))
{
glyphs[gi].contours[i].points[j-startPoint].x = xByte;
lastx = xByte;
}
else
{
glyphs[gi].contours[i].points[j-startPoint].x = -xByte;
lastx = -xByte;
}
}
else
{
// ____ If bit four is set, then this coordinate is the same as the ____
// ____ last one, so the relative offset (of zero) is stored. If bit ____
// ____ is not set, then read in two bytes and store it as a signed ____
// ____ value. ____
if(isBitSet(currentFlag,4))
{
glyphs[gi].contours[i].points[j-startPoint].x = 0;
lastx=0;
}
else
{
xWord=toSHORT(fontdata[pos],fontdata[pos+1]);pos+=2;
glyphs[gi].contours[i].points[j-startPoint].x = xWord;
lastx = xWord;
}
}
} // END: for each point in contour
// ____ Make the new starting point the one after the current ending point. ____
startPoint=endPoint+1;
} // END: for each contour
lasty=0; // The first coordinate is relative to zero. ____
startPoint = 0; // Start with the first coordinate in the glyph. ____
for(i=0;i<numberOfContours;i++)
{
// ____ Find the end point of this contour. ____
endPoint = endPtsOfContours[i];
// ____ Get each point in this contour. Coordinates in the font file are ____
// ____ indexed according to position in the entire glyph, while the ____
// ____ coordinates stored are indexed according to their position in the ____
// ____ current contour. 'startPoint' and 'endPoint' are glyph indexes, and ____
// ____ j is the index into the current contour. ____
// ____ Process Y-Coordinates. ____
for(j=startPoint;j<=endPoint;j++)
{
// ____ Get the current flag. ____
currentFlag = flags[j];
// ____ First we check to see if bit two is set. This would indicate that ____
// ____ the corresponding coordinate data in the table is 1 byte long. ____
// ____ If the bit is not set, then the coordinate data is 2 bytes long. ____
if(isBitSet(currentFlag,2))
{
// ____ Read in one byte. ____
yByte = fontdata[pos];pos++;
// ____ In this case, bit five is the sign of the byte just read in. ____
if(isBitSet(currentFlag,5))
{
glyphs[gi].contours[i].points[j-startPoint].y = yByte;
lasty = yByte;
}
else
{
glyphs[gi].contours[i].points[j-startPoint].y = -yByte;
lasty = -yByte;
}
}
else
{
// ____ If bit five is set, then this coordinate is the same as the ____
// ____ last one, so the relative offset (of zero) is stored. If bit ____
// ____ is not set, then read in two bytes and store it as a signed ____
// ____ value. ____
if(isBitSet(currentFlag,5))
{
glyphs[gi].contours[i].points[j-startPoint].y = 0;
lasty=0;
}
else
{
yWord=toSHORT(fontdata[pos],fontdata[pos+1]);pos+=2;
glyphs[gi].contours[i].points[j-startPoint].y = yWord;
lasty = yWord;
}
}
} // END: for each point in contour
// ____ Make the new starting point the one after the current ending point. ____
startPoint = endPoint+1;
} // END: for each contour in glyph
delete endPtsOfContours;
delete flags;
} // END: if not a composite.
} // END: for each glyph.
// ____ Finally, change the relative coordinates we just saved into absolute. ____
for(i=0;i<numGlyphs;i++)
{
tx=0;ty=0;
if(glyphs[i].numContours>0)
{
for(j=0;j<glyphs[i].numContours;j++)
{
for(k=0;k<glyphs[i].contours[j].numPoints;k++)
{
tx+=glyphs[i].contours[j].points[k].x;
ty+=glyphs[i].contours[j].points[k].y;
glyphs[i].contours[j].points[k].x=tx;
glyphs[i].contours[j].points[k].y=ty;
}
}
}
}
}
void TTFont::Print(void)
{
LONG i,j,k;
cout<<"----------------------------------------------------------------"<<endl;
cout<<" Font Information "<<endl;
cout<<"----------------------------------------------------------------"<<endl;
cout<< endl;
cout<<" Family: " <<Family() <<endl;
cout<<" Subfamily: " <<Subfamily() <<endl;
cout<<" Full Name: " <<FullName() <<endl;
cout<<" Version: " <<Version() <<endl;
cout<<" Postscript Name: " <<PSName() <<endl;
cout<<" Copyright: " <<Copyright() <<endl;
cout<<" Trademark: " <<Trademark() <<endl;
cout<< endl;
cout<<"Units per Em Square: " <<unitsPerEm <<endl;
cout<<"Number of Glyphs in this Font: " <<numGlyphs <<endl;
cout<<"Maximum Number of Points in a Glyph: " <<maxPoints <<endl;
cout<<"Maximum Number of Contours in a Glyph: " <<maxContours <<endl;
cout<< endl;
cout<<"Character Mappings:" <<endl;
for(i=0;i<256;i++)
cout<<" Character: "<<i<<" --- Glyph Index: "<<CharacterMap(i)<<endl;
cout<<endl;
cout<<"Glyphs:"<<endl;
for(i=0;i<numGlyphs;i++)
{
cout<<"Index: "<<i<<" Contours: "<<glyphs[i].numContours<<endl;
for(j=0;j<glyphs[i].numContours;j++)
{
cout<<" Contour: "<<j<<" Points: "<<glyphs[i].contours[j].numPoints<<endl;
for(k=0;k<glyphs[i].contours[j].numPoints;k++)
{
if (glyphs[i].contours[j].points[k].type==ON_CURVE)
cout<<" ON_CURVE Point: (";
else
cout<<" OFF_CURVE Point: (";
cout<<glyphs[i].contours[j].points[k].x<<",";
cout<<glyphs[i].contours[j].points[k].y<<")"<<endl;
}
}
}
}